package saves.items;

import game.Data;
import game.LScript;
import game.LScript.ParsingError;
import game.TextManager;
import images.ShipLoader;
import java.util.Arrays;
import java.util.HashMap;
import saves.EntidadeNave;

public class Equipamento extends Item {

    private static final long serialVersionUID = -8489114691394970690L;

    public enum LocalEquip {

        CARCACA(0), ASA(1), PROP_TRASEIRO(2), PROP_LATERAL(3), TRANSFORMADOR(4);
        private int posicao;

        LocalEquip(int i) {
            this.posicao = i;
        }

        public int p() {
            return posicao;
        }
        
        public boolean normallyHas(int c1, int c2) {
            switch (this) {
                case CARCACA:
                    return (c1 == 0 && c2 != 0) || (c1 == 2 && c2 != 0) || (c1 == 3) || (c1 == 4) || (c1 == 5);
                case ASA:
                    return (c1 == 0) || (c1 == 3 && c2 != 0) || (c1 == 4);
                case TRANSFORMADOR:
                    return (c1 == 3 && c2 != 0) || (c1 == 4 && c2 != 0) || (c1 == 2);
                case PROP_TRASEIRO:
                    return (c1 == 4 && c2 != 1);
                default:
                    return (c1 == 4 && c2 != 2);
            }
        }
        
        public int getPartState(int state, int side) {
            switch (this) {
                case CARCACA:
                case ASA:
                case TRANSFORMADOR:
                    return 0;
                case PROP_TRASEIRO:
                    return state >= 3 ? 1 : 0;
                default: { //PROP_LATERAL
                    if (state == 3)
                        return 1;
                    if (side == 0 && (state == 1 || state == 4))
                        return 1;
                    if (side == 1 && (state == 2 || state == 5))
                        return 1;
                    return 0;
                }
            }
        }

        public static LocalEquip toLocalEquip(int i) {
            switch (i) {
                case 0:
                    return CARCACA;
                case 1:
                    return ASA;
                case 2:
                    return PROP_TRASEIRO;
                case 3:
                    return PROP_LATERAL;
                default:
                    return TRANSFORMADOR;
            }
        }
        
        public static int size() {
            return 5;
        }
        
        @Override
        public String toString() {
            return TextManager.t().PARTS[posicao];
        }
    }
    
    private int codModelo;
    
    //estatísticas de jogo (podem ser calculados)
    private Efeito[] efeitos;
    private Condicao[] condicoes;
    
    //descricao
    private int codBase, codRevestimento; //determinará a cor
    private float modificador; //modificador aleatório de crafting relacionado com as propriedades do material

    private Equipamento() {
    }

    public Equipamento(int codModelo, int codBase, int codRevestimento, float modificador) {
        this.codModelo = codModelo;
        this.codBase = codBase;
        this.codRevestimento = codRevestimento;
        this.modificador = modificador;

        //todo gerar massa, volume, ir, ip
        this.massa = 1;
        this.volume = 1;
        this.indicePreco = .5f;
        this.indiceRaridade = .5f;
        
        //todo gerar atributos
        this.condicoes = new Condicao[] { };
        this.efeitos = new Efeito[] { };

        //é necessário gerar o nome, a descrição e o ícone
        HashMap<String, Object> map = new HashMap<>();
        map.put("material", Data.getElementoInfo(codBase).getNome());
        map.put("plating", Data.getElementoInfo(codRevestimento).getNome());
        map.put("part", getCodLocais());
        String[] tipos = new String[getTipos().length];
        for (int i = 0; i < tipos.length; i++)
            tipos[i] = TextManager.t().SHIPS[getTipos()[i] + 1];
        map.put("type", tipos);
        map.put("cond", condicoes);
        map.put("e", efeitos);
        try {
            this.nome = LScript.parse(TextManager.t().EQUIPS_NAMES[0], map);
            this.descricao = LScript.parse(TextManager.t().EQUIPS_NAMES[1], map) + "\n" + LScript.parse(TextManager.t().CONDITIONS_NAMES, map) + " " + LScript.parse(TextManager.t().EFFECTS_NAMES, map);
            this.descricao += "\n" + getVolumeAndMass();
        } catch (ParsingError e) {
            System.err.println("Error in language file: " + e.getLocalizedMessage()); //todo pretty it up error here
            System.exit(1);
        }
        this.icone = -1;
    }

    public int getCodModelo() {
        return codModelo;
    }

    public int getCodBase() {
        return codBase;
    }

    public int getCodRevestimento() {
        return codRevestimento;
    }
    
    public boolean is(LocalEquip eq) {
        LocalEquip[] locais = getCodLocais();
        for (int i = 0; i <= eq.p() && i < locais.length; i++) //vai só até a posição eq.p()
            if (locais[i] == eq)
                return true;
        return false;
    }

    public boolean podeEquipar(EntidadeNave n) {
        //primeiro checa as condições
        for (int i = 0; i < condicoes.length; i++)
            if (!condicoes[i].preenche(n))
                return false;

        //agora verifica se ele encaixa com a carcaça
        int tipoCarcaca = ShipLoader.modelos.get(n.getEquip(0).codModelo).getTipos()[0];
        if (getCodLocais()[0] != LocalEquip.CARCACA) { //não é carcaça
            if (n.getEquip(0) == null) //você não pode equipar nada se não tiver uma carcaça
                return false;
            int[] tipos = getTipos();
            boolean nok = true;
            for (int i = 0; i < tipos.length; i++)
                if (tipos[i] == tipoCarcaca) //compatível!
                    nok = false;
            if (nok)
                return false;
        } else if (tipoCarcaca != getTipos()[0]) //se o tipo dela é diferente do da carcaça anterior
            for (int i = 1; i < LocalEquip.size(); i++)
                if (n.getEquip(i) != null && !n.getEquip(i).temTipo(getTipos()[0]))
                    return false;
        
        //depois verifica, se for um prop lat ou trans en, se tem asa
        if ((is(LocalEquip.PROP_LATERAL) || is(LocalEquip.TRANSFORMADOR)) && !is(LocalEquip.ASA))
            if (!n.getEquip(0).is(LocalEquip.ASA) && (n.getEquip(1) == null || !n.getEquip(1).is(LocalEquip.ASA))) //somente os equips 0 e 1 podem ser as asas
                return false; //se não tem asa não tem como equipar prop lat nem trans en

        //finalmente verifica se não irá remover nada importante
        if (!is(LocalEquip.CARCACA)) { //se não for uma carcaça, poderia acabar removendo-a
            LocalEquip[] carcDep = n.getEquip(LocalEquip.CARCACA.p()).getCodLocais(); //dependencias da carcaça
            for (int i = 1; i < carcDep.length; i++) //posicao 0 é a própria carcaça
                if (is(carcDep[i]))
                    return false; //ao equipar esse item, acabará removendo a carcaça!
        }

        if (!is(LocalEquip.ASA)) { //se não for uma asa, poderia acabar removendo-a
            if (is(LocalEquip.PROP_LATERAL) && n.getEquip(LocalEquip.ASA.p()) != null && n.getEquip(LocalEquip.ASA.p()).is(LocalEquip.PROP_LATERAL)) //posso garantir que a asa não estará com a carcaça, pois se estivesse, teria pego no outro if ou não importaria
                return false; //nesse caso, a asa está junto com o prop.lat., e o novo prop. lat. não tem a asa
            if (is(LocalEquip.TRANSFORMADOR) && n.getEquip(LocalEquip.ASA.p()) != null && n.getEquip(LocalEquip.ASA.p()).is(LocalEquip.TRANSFORMADOR)) //posso garantir que a asa não estará com a carcaça, pois se estivesse, teria pego no outro if ou não importaria
                return false; //nesse caso, a asa está junto com o trans en, e o novo trans en não tem a asa
        }
        return true;
    }

    public boolean temTipo(int t) {
        for (int i = 0; i < getTipos().length; i++)
            if (getTipos()[i] == t)
                return true;
        return false;
    }

    public final int[] getTipos() {
        return ShipLoader.modelos.get(codModelo).getTipos();
    }

    public float modificar(int codStatus, int codBonus, float valor) {
        float valorParcial = valor;
        for (int i = 0; i < efeitos.length; i++)
            if (efeitos[i].apply(codStatus, codBonus))
                valorParcial = efeitos[i].modificar(valor);
        return valorParcial;
    }

    public final LocalEquip[] getCodLocais() {
        return ShipLoader.modelos.get(codModelo).getLocais();
    }
    
    public String getEffectsOn(int c1, int c2) {
        return getEffectsOn(efeitos, c1, c2);
    }
    
    public static String getEffectsOn(Efeito[] efeitos, int c1, int c2) {
        String t = "";
        for (int i = 0; i < efeitos.length; i++)
            if (efeitos[i].apply(c1, c2))
                t += efeitos[i].getDescription() + ", ";
        if (t.isEmpty())
            return null;
        return t.substring(0, t.length() - 2);
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof Equipamento) {
            Equipamento c = (Equipamento) o;

            if (getCodLocais().length != c.getCodLocais().length)
                return false;
            //os vetores estão sempre ordenados
            for (int i = 0; i < getCodLocais().length; i++)
                if (getCodLocais()[i] != c.getCodLocais()[i])
                    return false;

            return codModelo == c.codModelo && codBase == c.codBase && codRevestimento == c.codRevestimento && modificador == c.modificador;
        }
        return false;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 53 * hash + Arrays.deepHashCode(this.getCodLocais());
        hash = 53 * hash + this.codModelo;
        hash = 53 * hash + this.codBase;
        hash = 53 * hash + this.codRevestimento;
        hash = 53 * hash + Float.floatToIntBits(this.modificador);
        return hash;
    }
}
